package com.jyniubi.gmall.order.service.impl;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.jyniubi.gmail.common.bean.ResponseVo;
import com.jyniubi.gmail.common.exception.CartException;
import com.jyniubi.gmail.common.exception.OrderException;
import com.jyniubi.gmail.pms.entity.SkuAttrValueEntity;
import com.jyniubi.gmail.pms.entity.SkuEntity;
import com.jyniubi.gmail.sms.vo.ItemSaleVo;
import com.jyniubi.gmail.ums.entity.UserAddressEntity;
import com.jyniubi.gmail.ums.entity.UserEntity;
import com.jyniubi.gmail.wms.entity.WareSkuEntity;
import com.jyniubi.gmail.wms.vo.SkuLockVo;
import com.jyniubi.gmall.cart.entity.CartEntity;
import com.jyniubi.gmall.order.feign.*;
import com.jyniubi.gmall.order.interceptor.LoginInterceptor;
import com.jyniubi.gmall.order.pojo.UserInfo;
import com.jyniubi.gmall.order.service.OrderService;
import com.jyniubi.gmall.order.vo.OrderConfirmVo;
import com.jyniubi.gmall.oms.vo.OrderItemVo;
import com.jyniubi.gmall.oms.vo.OrderSubmitVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;


import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private GmallPmsClient pmsClient;

    @Autowired
    private GmallUmsClient umsClient;

    @Autowired
    private GmallSmsClient smsClient;

    @Autowired
    private GmallCartClient cartClient;

    @Autowired
    private GmallWmsClient wmsClient;
    @Autowired
    private GmallOmsClient omsClient;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    private static final String ORDER_KEY_PREFIX = "order:token:";

    @Override
    public OrderConfirmVo confirm() {
        OrderConfirmVo confirmVo = new OrderConfirmVo();
        // 不需要验空，因为userInfo是在拦截器中new出来得
        UserInfo userInfo = LoginInterceptor.getUserInfo();
        Long userId = userInfo.getUserId();
        // 根据userId查询收获地址列表
        CompletableFuture<Void> addressesCompletableFuture = CompletableFuture.runAsync(() -> {
            ResponseVo<List<UserAddressEntity>> addressesResponseVo = this.umsClient.queryAddressesByUserId(userId);
            List<UserAddressEntity> addressEntities = addressesResponseVo.getData();
            if (!CollectionUtils.isEmpty(addressEntities)) {
                confirmVo.setAddresses(addressEntities);
            }
        }, threadPoolExecutor);
        // 根据userId查询选中的shangp
        CompletableFuture<List<CartEntity>> checkCartsCompletableFuture = CompletableFuture.supplyAsync(() -> {
            ResponseVo<List<CartEntity>> checkCartsResponseVo = this.cartClient.queryCheckedCarts(userId);
            List<CartEntity> checkCarts = checkCartsResponseVo.getData();
            if (CollectionUtils.isEmpty(checkCarts)) {
                throw new CartException("请选择要购买的商品！");
            }
            return checkCarts;
        }, threadPoolExecutor);
        CompletableFuture<Void> itemCompletableFuture = checkCartsCompletableFuture.thenAcceptAsync(checkCarts -> {
            List<OrderItemVo> items = checkCarts.stream().map(checkCart -> {
                OrderItemVo orderItemVo = new OrderItemVo();
                Long skuId = checkCart.getSkuId();
                orderItemVo.setSkuId(skuId);
                orderItemVo.setCount(checkCart.getCount());
                CompletableFuture<Void> skuEntityComletableFuture = CompletableFuture.runAsync(() -> {
                    ResponseVo<SkuEntity> skuEntityResponseVo = this.pmsClient.querySkuById(skuId);
                    SkuEntity skuEntity = skuEntityResponseVo.getData();
                    orderItemVo.setDefaultImage(skuEntity.getDefaultImage());
                    orderItemVo.setPrice(skuEntity.getPrice());
                    orderItemVo.setTitle(skuEntity.getTitle());
                    orderItemVo.setWeight(skuEntity.getWeight());
                }, threadPoolExecutor);
                CompletableFuture<Void> skuAttrsCompletableFuture = CompletableFuture.runAsync(() -> {
                    ResponseVo<List<SkuAttrValueEntity>> skuAttrsResponseVo = this.pmsClient.querySaleAttrValuesBySkuId(skuId);
                    List<SkuAttrValueEntity> skuAttrsEntities = skuAttrsResponseVo.getData();
                    if (!CollectionUtils.isEmpty(skuAttrsEntities)) {
                        orderItemVo.setSaleAttrs(skuAttrsEntities);
                    }
                }, threadPoolExecutor);
                CompletableFuture<Void> salesCompletableFuture = CompletableFuture.runAsync(() -> {
                    ResponseVo<List<ItemSaleVo>> salesResponseVo = this.smsClient.querySalesBySkuId(skuId);
                    List<ItemSaleVo> saleVoList = salesResponseVo.getData();
                    if (!CollectionUtils.isEmpty(saleVoList)) {
                        orderItemVo.setSales(saleVoList);
                    }
                }, threadPoolExecutor);
                CompletableFuture<Void> storeCompletableFuture = CompletableFuture.runAsync(() -> {
                    ResponseVo<List<WareSkuEntity>> storeResponseVo = this.wmsClient.querySkuById(skuId);
                    List<WareSkuEntity> wareSkuEntities = storeResponseVo.getData();
                    if (!CollectionUtils.isEmpty(wareSkuEntities)) {
                        orderItemVo.setStore(wareSkuEntities.stream().anyMatch(wareSkuEntity -> wareSkuEntity.getStock() - wareSkuEntity.getStockLocked() > 0));
                    }
                }, threadPoolExecutor);
                CompletableFuture.allOf(skuEntityComletableFuture,skuAttrsCompletableFuture,salesCompletableFuture,storeCompletableFuture).join();
                return orderItemVo;
            }).collect(Collectors.toList());
            confirmVo.setItems(items);

        }, threadPoolExecutor);
        // 购物积分
        CompletableFuture<Void> userEntityCompletableFuture = CompletableFuture.runAsync(() -> {
            ResponseVo<UserEntity> userEntityResponseVo = this.umsClient.queryUserById(userId);
            UserEntity userEntity = userEntityResponseVo.getData();
            if (userEntity != null) {
                confirmVo.setBounds(userEntity.getIntegration());
            }
        }, threadPoolExecutor);

        CompletableFuture<Void> orderTokenCompletableFuture = CompletableFuture.runAsync(() -> {
            // 防重的唯一标识：分布式id方案
            String orderToken = IdWorker.getIdStr();
            // 页面一份 redis一份
            confirmVo.setOrderToken(orderToken);
            this.redisTemplate.opsForValue().set(ORDER_KEY_PREFIX + orderToken, orderToken, 2, TimeUnit.HOURS);
        }, threadPoolExecutor);
        CompletableFuture.allOf(addressesCompletableFuture,itemCompletableFuture,userEntityCompletableFuture,orderTokenCompletableFuture).join();
        return confirmVo;
    }

    @Override
    public void submit(OrderSubmitVo orderSubmitVo) {
        // 防重校验：如果重复提交则直接抛出异常
        String orderToken = orderSubmitVo.getOrderToken();
        if (StringUtils.isBlank(orderToken)){
            throw new OrderException("非法请求！");
        }
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] " +
                "then " +
                "   return redis.call('del', KEYS[1]) " +
                "else " +
                "   return 0 " +
                "end";
        Boolean flag = this.redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(ORDER_KEY_PREFIX + orderToken), orderToken);
        if (!flag){
            throw new OrderException("请不要重复提交！");
        }
        // 验总价，如果页面总价和数据库查询的总价不一致则抛出异常
        List<OrderItemVo> items = orderSubmitVo.getItems();
        if (CollectionUtils.isEmpty(items)){
            throw new OrderException("请选择要购买的商品！");
        }
        BigDecimal totalPrice = orderSubmitVo.getTotalPrice();
        BigDecimal currentTotalPrice = items.stream().map(item -> {
            ResponseVo<SkuEntity> skuEntityResponseVo = this.pmsClient.querySkuById(item.getSkuId());
            SkuEntity skuEntity = skuEntityResponseVo.getData();
            if (skuEntity == null) {
                return new BigDecimal(0);
            }
            return skuEntity.getPrice().multiply(item.getCount());
        }).reduce((a, b) -> a.add(b)).get();
        if (currentTotalPrice.compareTo(totalPrice) != 0){
            throw new OrderException("页面已过期，请刷新后重试！");
        }
        // 验库存并锁库存 保证原子性
        List<SkuLockVo> skuLockVos = items.stream().map(item->{
            SkuLockVo skuLockVo = new SkuLockVo();
            skuLockVo.setSkuId(item.getSkuId());
            skuLockVo.setCount(item.getCount().intValue());
            return skuLockVo;
        }).collect(Collectors.toList());
        ResponseVo<List<SkuLockVo>> skuLockResponseVo = this.wmsClient.checkAndLock(skuLockVos, orderToken);
        List<SkuLockVo> skuLockVoList = skuLockResponseVo.getData();
        if (!CollectionUtils.isEmpty(skuLockVoList)){
            throw new OrderException(JSON.toJSONString(skuLockVoList));
        }
        // 测试定时解锁库存
        //int i = 1/0;

        // 创建订单
        UserInfo userInfo = LoginInterceptor.getUserInfo();
        Long userId = userInfo.getUserId();
        try {
            this.omsClient.saveOrder(orderSubmitVo, userId);
            this.rabbitTemplate.convertAndSend("ORDER.EXCHANGE","order.ttl",orderToken);
        } catch (Exception e) {
            e.printStackTrace();
            // 创建订单异常，解锁库存
            this.rabbitTemplate.convertAndSend("ORDER.EXCHANGE","order.fail",orderToken);
            throw new OrderException("服务器内部错误！");
        }
        // 异步删除购物车中对应的记录
        Map<String,Object> msg = new HashMap<>();
        msg.put("userId",userId);
        msg.put("skuIds",JSON.toJSONString(items.stream().map(OrderItemVo::getSkuId).collect(Collectors.toList())));
        this.rabbitTemplate.convertAndSend("ORDER.EXCHANGE","cart.delete",msg);
    }
}
